using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using DTT.AreaOfEffectRegions;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GameAOT;
using LFloatMath.Math;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

namespace IQIGame.Onigao.GamePlay
{
    public class UnitViewComponent : TComponent
    {
        public UnitView unit;

        public UnitViewRes curRes { get; private set; }
        private List<UnitViewRes> _resList = new List<UnitViewRes>();

        private BattleUnitEntityView _entity;
        public ResLoader resLoader;

        private BattleUnitEntityView _widget;

        #region 组件

        public GameObject gameObject { get; private set; }
        private GameObject widgetGameObject { get; set; }
        private GameObject model { get; set; }
        private GameObject widgetModel { get; set; }
        public Transform transform { get; private set; }
        public EntityDriver driver { get; private set; }
        public Animator animator { get; private set; }
        public PlayableDirector playableDirector { get; private set; }
        public ExSignalReceiverBinding signalReceiver { get; private set; }
        public CharacterController characterCtrl { get; private set; }
        private BehaviorFeatureComp behaviorFeatureComp { get; set; }

        #endregion

        public Vector3 position => this.transform ? this.transform.localPosition : Vector3.zero;
        public Quaternion rotation => this.transform ? this.transform.localRotation : Quaternion.identity;
        public Vector3 forward => this.transform ? this.transform.forward : Vector3.zero;

        private bool _active;
        public bool active
        {
            get => this._active;
            set
            {
                this._active = value;
                if (this.model)
                {
                    this.model.SetActive(this._active);
                }
                if (this.widgetModel)
                {
                    this.widgetModel.SetActive(this._widgetActive && this.active);
                }
#if UNITY_EDITOR
                if (!BattleClientUtil.IsEditorMode)
#endif
                {
                    MsgDispatcher.Broadcast(MsgEventType.BattleView_UnitActive, this.unit, this._active);
                }
            }
        }

        private bool _widgetActive;
        public bool widgetActive
        {
            get => this._widgetActive;
            set
            {
                this._widgetActive = value;
                if (this.widgetModel)
                {
                    this.widgetModel.SetActive(this._widgetActive && this.active);
                }
            }
        }

        private bool _enabled;
        public bool enabled
        {
            get => this._enabled;
            set
            {
                this._enabled = value;
                if (this.gameObject)
                {
                    this.gameObject.SetActive(this._enabled);
                }
                this.active = this.enabled;
            }
        }

        private bool _refreshingGameObject;
        private bool _inSyncPos;

        protected override void OnDispose()
        {
            if (this.driver)
            {
                BattleManager.Instance.ctrl?.data.RemoveDriverUnitPair(this.driver);
            }
            this._entity?.Put();
            if (this.mInCtrlEffect != null)
            {
                this.mInCtrlEffect.Put();
                this.mInCtrlEffect = null;
            }
            if (this.resLoader != null)
            {
                this.resLoader.Put2Pool();
                this.resLoader = null;
            }
        }

        public override void OnCreate(TEntity entity)
        {
            this.unit = entity as UnitView;
        }

        private bool _widgetMoving;
        private bool _widgetRoting;
        public override void OnUpdate(float deltaTime)
        {
            base.OnUpdate(deltaTime);
            if (this._inSyncPos)
            {
                // 同步位置后插值过去 此处碰撞已经放置到目标点
                var rTargetPos = this.unit.pos.ToVector3XZ();
                this.transform.localPosition = Vector3.Lerp(this.position, rTargetPos, 0.3f);
                var rTargetRot = Quaternion.LookRotation(this.unit.forward.ToVector3XZ());
                this.transform.rotation = Quaternion.Lerp(this.rotation, rTargetRot, 0.3f);
                if ((this.position - rTargetPos).sqrMagnitude <= 0.01f && (this.rotation.eulerAngles - rTargetRot.eulerAngles).sqrMagnitude <= 4f)
                {
                    this.transform.localPosition = rTargetPos;
                    this.transform.rotation = rTargetRot;
                    this._inSyncPos = false;
                }
            }
            if (this.widgetGameObject)
            {
                var fPosSubSqr = (this.position - this.widgetGameObject.transform.position).sqrMagnitude;
                if (!this._widgetMoving)
                {
                    this._widgetMoving = fPosSubSqr > 0.1f;
                }

                var rSelfRotEuler = this.rotation.eulerAngles;
                var rWidgetRotEuler = this.widgetGameObject.transform.rotation.eulerAngles;
                if (rSelfRotEuler.y > rWidgetRotEuler.y)
                {
                    rSelfRotEuler.y -= 360;
                }
                var fRotSub = rWidgetRotEuler.y - rSelfRotEuler.y;
                if (fRotSub > 180)
                {
                    fRotSub = Math.Abs(fRotSub - 360);
                }
                if (!this._widgetRoting)
                {
                    this._widgetRoting = fRotSub > 30;
                }

                if (this._widgetMoving)
                {
                    this._widgetMoving = fPosSubSqr > 0.01f;
                    // 这里用世界坐标赋值
                    this.widgetGameObject.transform.position = this._widgetMoving ?
                        Vector3.Lerp(this.widgetGameObject.transform.position, this.transform.position, 0.1f) :
                        this.transform.position;
                }
                if (this._widgetRoting)
                {
                    this._widgetRoting = fRotSub > 1;
                    if (this._widgetRoting)
                    {
                        float fTargetY;
                        if (rWidgetRotEuler.y - rSelfRotEuler.y > 20)
                        {
                            fTargetY = rWidgetRotEuler.y - 5;
                        }
                        else
                        {
                            fTargetY = Mathf.Lerp(rWidgetRotEuler.y, rSelfRotEuler.y, 0.1f);
                        }
                        var rTargetEuler = new Vector3(rWidgetRotEuler.x, fTargetY, rWidgetRotEuler.z);
                        this.widgetGameObject.transform.rotation = Quaternion.Euler(rTargetEuler);
                    }
                    else
                    {
                        this.widgetGameObject.transform.rotation = this.transform.rotation;
                    }
                }
            }
        }

        public async UniTask Initialize()
        {
            if (this.resLoader != null)
            {
                this.resLoader.Put2Pool();
            }
            this.resLoader = ResLoader.Create();

            this._resList.Add(new UnitViewRes()
            {
                resPath = this.unit.resPath,
                resName = this.unit.roleName,
                entityType = this.unit.entityType,
                widgetPath = this.unit.widgetPath
            });
            await this.RefreshGameObject();

            this.active = false;
        }

        private async UniTask RefreshGameObject()
        {
            var rResPath = this._resList[^1];
            if (!this.curRes.Equals(rResPath))
            {
                var rPath = rResPath.resPath; // this.GetEntityLoadPath(rResPath.entityType, rResPath.resName);
#if UNITY_EDITOR
                if (BattleClientUtil.IsEditorMode)
                {
                    // 删除旧的
                    if (this.gameObject)
                    {
                        BattleClientUtil.SafeDestroy(this.gameObject);
                    }
                    var rPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/" + rPath);
                    this.gameObject = BattleClientUtil.CreateGameobject(rPrefab);
                    this.transform = this.gameObject.transform;
                    if (!string.IsNullOrEmpty(rResPath.widgetPath))
                    {
                        var rWidgetPrefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/" + rResPath.widgetPath);
                        this.widgetGameObject = BattleClientUtil.CreateGameobject(rWidgetPrefab);
                    }
                }
                else
#endif
                {
                    // 删除旧的
                    if (this._entity != null && this._entity.isLoaded)
                    {
                        this._entity.Put();
                    }
                    var rEntity = ManagerCenter.Entity.Get(rPath, (int)EntityPoolGroupType.BattleUnit) as BattleUnitEntityView;
                    this._entity = rEntity;
                    await rEntity.Load();
                    if (!string.IsNullOrEmpty(rResPath.widgetPath))
                    {
                        var rWidgetEntity = ManagerCenter.Entity.Get(rResPath.widgetPath, (int)EntityPoolGroupType.BattleUnit) as BattleUnitEntityView;
                        this._widget = rWidgetEntity;
                        await rWidgetEntity.Load();
                        if (rWidgetEntity != this._widget)
                        {
                            rWidgetEntity.Put();
                        }
                    }
                    if (rEntity != this._entity) // 加载期间又换了别的角色
                    {
                        rEntity.Put();
                        return;
                    }
                    this.gameObject = this._entity.gameObject;
                    this.transform = this._entity.transform;
                    this.widgetGameObject = this._widget?.gameObject;
                }
            }
            if (!this.transform)
            {
                return;
            }
            this.curRes = rResPath;

            if (BattleManager.Instance.ctrl != null)
            {
                this.transform.SetParent(BattleManager.Instance.ctrl.sceneCtrl.roleRoot);
            }
            this.transform.localPosition = this.unit.pos.ToVector3XZ();
            this.transform.rotation = Quaternion.LookRotation(this.unit.forward.ToVector3XZ());

            this.characterCtrl = this.gameObject.GetOrAddComponent<CharacterController>();
            if (this.driver != null)
            {
                BattleClientUtil.SetEntityDriverToUnitView(this.driver, this.unit);
            }
            this.driver = this.gameObject.GetComponent<EntityDriver>();
            BattleClientUtil.SetEntityDriverToUnitView(this.driver, this.unit);
            this.behaviorFeatureComp = this.gameObject.GetComponent<BehaviorFeatureComp>();
            this.characterCtrl.radius = this.unit.radius;
            this.model = this.transform.Find("model").gameObject;
            this.animator = this.model.GetComponent<Animator>();

            this.playableDirector = this.gameObject.GetComponent<PlayableDirector>();
            this.signalReceiver = this.gameObject.GetComponent<ExSignalReceiverBinding>();

            if (this.widgetGameObject)
            {
                this.widgetModel = this.widgetGameObject.transform.Find("model").gameObject;

                this.widgetGameObject.transform.position = this.transform.position;
                this.widgetGameObject.transform.rotation = this.transform.rotation;
                this.widgetActive = true;
            }

            this.unit.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent).Refresh();
        }

        public void Enable()
        {
            if (this.active)
            {
                return;
            }
            this.active = true;
            var rAnimeComponent = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimeComponent.Enable(true);
        }

        public async UniTask SyncPos(bool startSync = true)
        {
            this._inSyncPos = startSync;
            while (this._inSyncPos)
            {
                await UniTask.Yield();
            }
        }

        public void PlayAnime(int nHash)
        {
            var rAnimeComponent = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimeComponent?.PlayAnime(nHash);
        }

        public void Die()
        {
            var rTimelineComp = this.unit.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
            rTimelineComp?.Stop();
            var rAnimeComponent = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimeComponent.SetTrigger(AnimationConst.Param_Die);
        }

        public void Relive()
        {
            if (!this.enabled)
            {
                this.enabled = true;
            }
            var rAnimeComponent = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
            rAnimeComponent?.Relive();
        }

        public async UniTask Morph(string resName, int resType)
        {
            // 皮肤有可能会更换掉变身后的皮
            resName = UnitViewSkinFactory.GetSkin(this.unit.skinID, resName);
            var rPath = this.GetEntityLoadPath(resType, resName);
            this._resList.Add(new UnitViewRes()
            {
                resName = resName,
                resPath = rPath,
                entityType = resType,
                widgetPath = this.unit.widgetPath
            });
            await this.RefreshGameObject();
        }

        public async UniTask RemoveMorph(string resName)
        {
            resName = UnitViewSkinFactory.GetSkin(this.unit.skinID, resName);
            var nIndex = this._resList.FindIndex((x) => x.resName == resName);
            if (nIndex != -1)
            {
                this._resList.RemoveAt(nIndex);
                await this.RefreshGameObject();
            }
        }

        public void OnHit()
        {
            var rTimelineComp = this.unit.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
            if (rTimelineComp.curState != BattleTimelineComponent.ETimelineState.Playing && this.behaviorFeatureComp)
            {
                this.behaviorFeatureComp.PlayGroup("选中闪红");
            }
            if (this.unit.GetComponent<DeadViewComponent>(ETComponentType.DeadViewComponent) == null)
            {
                var rAnimeComponent = this.unit.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent);
                rAnimeComponent?.PlayAnime(AnimationConst.Name_Hit);
            }
        }

        public void OnSkillSelected()
        {
            var rTimelineComp = this.unit.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
            if (rTimelineComp.curState != BattleTimelineComponent.ETimelineState.Playing && this.behaviorFeatureComp)
            {
                this.behaviorFeatureComp.PlayGroup("选中保持颜色");
            }
        }

        public void OnStopSkillSelected()
        {
            var rTimelineComp = this.unit.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
            if (rTimelineComp.curState != BattleTimelineComponent.ETimelineState.Playing && this.behaviorFeatureComp)
            {
                this.behaviorFeatureComp.StopAndRestore();
            }
        }

        #region 当前指挥角色指示特效

        private BattleSceneEntityView mInCtrlEffect;

        public async UniTask InControl()
        {
            BattleManager.Instance.ctrl.orderCtrl.ShowInput(true);
            var rDummyComp = this.unit.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent);

            var rRootTrans = rDummyComp.GetBindPoint(1);

            this.mInCtrlEffect = ManagerCenter.Entity.Get(PathConstant.GetBattleCommonPrefabPath("InControl"),
                (int)EntityPoolGroupType.BattleSceneEntity) as BattleSceneEntityView;
            await this.mInCtrlEffect.Load();
            var rGoComp = this.mInCtrlEffect.gameObject.GetComponent<CircleRegion>();
            if (rGoComp)
            {
                rGoComp.Radius = this.unit.radius;
            }
            this.mInCtrlEffect.transform.SetParent(rRootTrans);
            this.mInCtrlEffect.transform.localPosition = Vector3.zero;

            var rMoveComp = this.unit.GetComponent<BattleMoveComponent>(ETComponentType.BattleMoveComponent);
            await rMoveComp.SetInputMove(this.unit.pos.ToVector3XZ());
        }

        public void OutControl()
        {
            if (BattleManager.Instance.ctrl == null)
            {
                return;
            }
            BattleManager.Instance.ctrl.orderCtrl.ShowInput(false);
            if (this.mInCtrlEffect != null)
            {
                this.mInCtrlEffect.Put();
                this.mInCtrlEffect = null;
            }
            var rMoveComp = this.unit.GetComponent<BattleMoveComponent>(ETComponentType.BattleMoveComponent);
            rMoveComp?.OutInput();
        }

        #endregion

        #region Dialog

        private UILightDialog dialogWindow;
        protected UINLightDialogItem _lighDialogItem;

        public async UniTask ShowHeadLightDialog(string text, float typeWriterSpeed, float lifeTime)
        {
            if (_lighDialogItem == null)
            {
                dialogWindow = ManagerCenter.UI.GetEntityUI<UILightDialog>();
                if (dialogWindow == null)
                {
                    return;
                }
                _lighDialogItem = dialogWindow.CreateBattleDialogItem();
            }
            //else
            //{
            //    //播放中，忽略
            //    return;
            //}
            _lighDialogItem.SetFllowTarget(this._entity.transform, BattleConst.DialogWorldOffset, BattleConst.DialogSceneOffset);
            await _lighDialogItem.StartTextDialog(text, typeWriterSpeed, lifeTime);

            dialogWindow = ManagerCenter.UI.GetEntityUI<UILightDialog>();
            dialogWindow?.PutPoolBattleDialogItem(_lighDialogItem);
            _lighDialogItem = null;
        }

        #endregion

        private string GetEntityLoadPath(int entityType, string resName)
        {
            if (entityType == 0)
            {
                return PathConstant.GetCharacterBattlePrefabPath(resName);
            }
            else
            {
                return PathConstant.GetMonsterBattlePrefabPath(resName);
            }
        }

        public struct UnitViewRes
        {
            public int entityType;
            public string resName;
            public string resPath;
            public string widgetPath;

            public override bool Equals(object obj)
            {
                return obj is UnitViewRes res &&
                       res.resName == resName &&
                       res.entityType == entityType &&
                       res.resPath == resPath &&
                       res.widgetPath == this.widgetPath;
            }

            public override int GetHashCode()
            {
                return HashCode.Combine(this.entityType, this.resName, this.resPath, this.widgetPath);
            }
        }
    }

    public static class UnitViewSkinFactory
    {
        private static Dictionary<int, Dictionary<string, string>> mSkinDict = new Dictionary<int, Dictionary<string, string>>();

        public static string GetSkin(int curSkinID, string targetRes)
        {
            if (mSkinDict.Count <= 0)
            {
                var rTable = TableCenter.battleMorphs.DataList;
                for (int i = 0; i < rTable.Count; i++)
                {
                    var rConfig = rTable[i];
                    if (!mSkinDict.TryGetValue(rConfig.CurHeroSkin, out var rCache))
                    {
                        rCache = new Dictionary<string, string>();
                        mSkinDict[rConfig.CurHeroSkin] = rCache;
                    }
                    rCache[rConfig.DesEntityAsset] = rConfig.MorEntityAsset;
                }
            }

            if (mSkinDict.TryGetValue(curSkinID, out var rDict) && rDict.TryGetValue(targetRes, out var rSkin))
            {
                return rSkin;
            }
            return targetRes;
        }
    }
}